//
// Created by hjie on 23-8-24.
//

#include "input_output_operate.h"
#include "base_util.h"
#include "input_output_context.h"
#include <cstdio>
#include <libavcodec/codec_id.h>
#include <libavformat/avformat.h>

AVPixelFormat GetHwPixelFormat(AVCodecContext * codec_ctx, const AVPixelFormat * av_pixel_formats)
{
    InputFileData * input_data = (InputFileData*)codec_ctx->opaque;
    while (*av_pixel_formats != AV_PIX_FMT_NONE)
    {
        if (*av_pixel_formats == input_data->m_hw_pixel_fmt)
        {
            return *av_pixel_formats;
        }
        av_pixel_formats++;
    }
    printf("no find qsv pix fmt.\n");
    return AV_PIX_FMT_NONE;
}

AVPixelFormat GetFormatForInputHwFrameCtx(AVCodecContext  * codec_ctx, const AVPixelFormat * av_pixel_formats)
{
    auto * input_data = (InputFileData*)codec_ctx->opaque;
    if (!input_data)
    {
        PrintLog(-1);
        return AV_PIX_FMT_NONE;
    }
    while (*av_pixel_formats != AV_PIX_FMT_NONE)
    {
        if (*av_pixel_formats == input_data->m_hw_pixel_fmt)
        {
            if (!input_data->m_get_format_init_status)
            {
                AVBufferRef * frame_buff      = av_hwframe_ctx_alloc(input_data->m_hw_buffer_ref);;
                auto * frame_ctx = (AVHWFramesContext*)frame_buff->data;

                frame_ctx->format    = input_data->m_hw_pixel_fmt;
                frame_ctx->sw_format = input_data->m_video_decoder_ctx->sw_pix_fmt;
                frame_ctx->width     = input_data->m_video_decoder_ctx->width;
                frame_ctx->height    = input_data->m_video_decoder_ctx->height;
                frame_ctx->initial_pool_size = 32;
                int result = av_hwframe_ctx_init(frame_buff);
                if (result < 0)
                {
                    char err_buff[1500] = {0};
                    av_make_error_string(err_buff, sizeof(err_buff), result);
                    printf("error code : %d and error message : %s.\n", result, err_buff);
                    av_buffer_unref(&frame_buff);
                    return AV_PIX_FMT_NONE;
                }
                else
                {
                    input_data->m_video_decoder_ctx->hw_frames_ctx = av_buffer_ref(frame_buff);
                    av_buffer_unref(&frame_buff);
                }
                input_data->m_get_format_init_status = false;
            }
            return *av_pixel_formats;
        }
        av_pixel_formats++;
    }
    return AV_PIX_FMT_NONE;
}


void GetFormatForOutputHwFrameCtx(AVCodecContext  * codec_ctx)
{
    auto input_data = (InputFileData*)codec_ctx->opaque;
    auto output_data = (OutputFileData*)input_data->m_output_data;

    if (output_data && !output_data->m_get_format_init_status)
    {
        AVBufferRef * frame_buff = av_buffer_ref(codec_ctx->hw_frames_ctx);
        int ret = 0;
        {
            output_data->m_video_encoder_ctx->hw_frames_ctx = av_buffer_ref(frame_buff);
            av_buffer_unref(&frame_buff);
            output_data->m_video_encoder_ctx->pix_fmt = output_data->m_hw_pixel_fmt;
            output_data->m_video_encoder_ctx->width   = input_data->m_video_decoder_ctx->width;
            output_data->m_video_encoder_ctx->height  = input_data->m_video_decoder_ctx->height;
            if ((ret = avcodec_open2(output_data->m_video_encoder_ctx, output_data->m_video_codec_for_hw, nullptr)) < 0)
            {
                PrintLog(ret);
                return;
            }
            AVStream * new_stream = avformat_new_stream(output_data->m_output_fmt_ctx, output_data->m_video_codec_for_hw);

            avcodec_parameters_from_context(new_stream->codecpar, output_data->m_video_encoder_ctx);
            new_stream->time_base = output_data->m_video_encoder_ctx->time_base;
            new_stream->codecpar->format = output_data->m_hw_pixel_fmt;
            output_data->m_video_stream_index  = new_stream->index;

            new_stream->avg_frame_rate = input_data->m_format_ctx->streams[input_data->m_video_stream_index]->avg_frame_rate;
            new_stream->r_frame_rate = input_data->m_format_ctx->streams[input_data->m_video_stream_index]->r_frame_rate;
            // ret = avformat_write_header(output_data->m_output_fmt_ctx, nullptr);
            // if (ret < 0)
            // {
            //     PrintLog(ret);
            // }
            output_data->OpenOutputFile();
        }
        output_data->m_get_format_init_status = true;
    }
}

int SelectCodecTagByCodecId(int codec_id)
{
    switch(codec_id)
    {
    case AV_CODEC_ID_H264:
    {
        return MKTAG('a', 'v', 'c', '1');
    }
    case AV_CODEC_ID_HEVC:
    {
        return MKTAG('h', 'e', 'v', '1');
    }
    case AV_CODEC_ID_VP9:
    {
        return MKTAG('v', 'p', '0', '9');
    }
    }
    return 0;
}